 package com.gmrz.uaf.protocol.v1.validaton.reg;
 
 import com.gmrz.uaf.common.Constants;
import com.gmrz.uaf.common.GuiceUtil;
import com.gmrz.uaf.crypto.CryptoEngine;
import com.gmrz.uaf.crypto.spi.UAFAuthAlgorithmSuite;
import com.gmrz.uaf.protocol.v1.processor.exception.UAFErrorCode;
import com.gmrz.uaf.protocol.v1.schema.AuthenticatorSpec;
import com.gmrz.uaf.protocol.v1.schema.KeyRegistrationData;
import com.gmrz.uaf.protocol.v1.validaton.Validator;
import com.gmrz.util.Convert;
 import com.gmrz.util.Strings;
 import com.gmrz.uaf.crypto.sm.SM3Utils;
 import com.google.inject.Inject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.Arrays;
 
 public class KeyRegDataValidator
   implements Validator<KeyRegistrationData>
 {
   private static final Logger LOG = LogManager.getLogger(KeyRegDataValidator.class);
   private AuthenticatorSpec spec;
   private String fcpbase64;
   private CryptoEngine cryptoEngine;
 
   public KeyRegDataValidator(AuthenticatorSpec spec, String fcpBase64)
   {
     this.spec = spec;
     this.fcpbase64 = fcpBase64;
     GuiceUtil.getProcessorInjector().injectMembers(this);
   }
 
   @Inject
   public void setCryptoEngine(CryptoEngine cryptoEngine) {
     this.cryptoEngine = cryptoEngine;
   }
 
   public void validate(KeyRegistrationData data) throws RegAssertionValidationException
   {
     String msg = "";
     String specAAID = this.spec.getAAID().format();
     String dataAAID = data.getAAID();
     if ((dataAAID == null) || (!specAAID.equals(dataAAID))) {
       msg = "AAID value in KRD[" + dataAAID + "] doesn't matches with the value in spec[" + specAAID + "]";
       logAndThrowRAVE(msg, UAFErrorCode.PROTOCOL_AAID_VALIDATION_FAILED);
     }
 
     int authenticatorVersion = data.getAuthenticatorVersion();
     if (authenticatorVersion < this.spec.getAuthenticatorVersion().intValue()) {
       msg = "Authenticator[" + dataAAID + "] version should be greater than or equal to [" + this.spec.getAuthenticatorVersion() + "].";
       LOG.warn(msg);
     }
 
     int authenticationMode = data.getAuthenticatonMode();
     if (authenticationMode != Constants.AuthenticationMode.REG_MODE.getValue()) {
       msg = "Value of authentication mode for registration should be [0x01]";
       logAndThrowRAVE(msg, UAFErrorCode.PROTOCOL_AUTHENTICATION_MODE_INVALID);
     }
 
     int keyFormat = data.getKeyFormat();
     if (keyFormat != this.spec.getKeyFormat()) {
       msg = "Authenticator public key format is invalid[" + keyFormat + "]";
       logAndThrowRAVE(msg, UAFErrorCode.PROTOCOL_KEY_FORMAT_NOT_FOUND);
     }
 
     int algoSuit = data.getSignAlgorithm();
     if (algoSuit != this.spec.getAlgorithm()) {
       msg = "Authenticator AlgoSuit is invalid[" + algoSuit + "]";
       logAndThrowRAVE(msg, UAFErrorCode.PROTOCOL_ALGORITHM_NOT_FOUND);
     }
 
     int regCounter = data.getRegisterCounter();
     if (regCounter == 0) {
       LOG.info("Reg counter is not supported by Authenticator with AAID[" + this.spec.getAAID() + "]");
     }
 
     try
     {
       byte[] fcpHashActual = null;
         UAFAuthAlgorithmSuite suite = this.cryptoEngine.getAuthAlgorithmSuite(algoSuit);
         fcpHashActual = suite.hash(Convert.toUtf8(this.fcpbase64));
 
       byte[] fcpHashExpected = data.getHashedFinalChallenge();
       if (!Arrays.equals(fcpHashExpected, fcpHashActual)) {
         msg = "Failed to validate the FCP hash for Reg response.";
         logAndThrowRAVE(msg, UAFErrorCode.PROTOCOL_FCP_HASH_VALIDATION_FAILED);
       }
     }
     catch (Exception e) {
       msg = "Failed to validate the FCP hash for Reg response.";
       logAndThrowRAVE(msg, UAFErrorCode.PROTOCOL_FCP_HASH_VALIDATION_FAILED);
     }
   }
 
   void logAndThrowRAVE(String msg, UAFErrorCode ec) throws RegAssertionValidationException {
     LOG.error(msg);
     throw new RegAssertionValidationException(ec, msg);
   }
 }
